import 'package:expressions/expressions.dart';
import 'package:my_flutter_core/extensions/string_extension.dart';
import 'package:sprintf/sprintf.dart';

class ValidatorPattern {
  ValidatorPattern(this.pattern, this.errorMessage, {this.matchCheck = true});

  // The string of regular expression pattern.
  final String pattern;

  // The string of error message which returned to field validator.
  // When pattern matches in matchCheck = true or pattern does NOT match in matchCheck = false the errorMessage is not null/empty.
  final String errorMessage;

  // True means check if the pattern matches. False means check if the pattern does NOT matche.
  final bool matchCheck;
}

class ExpressionValidationResult {
  ExpressionValidationResult(this.expressionResult, this.errorMessage);

  final double? expressionResult;
  final String? errorMessage;
}

/// This is the validator for form text field.
///
/// All kind of validators based on provided properties.
/// Example:
/// - If [length] is set then [_checkLength] will be applied.
/// - If [minLength] is set then [_checkMinLength] will be applied.
/// - If [minLength] and [maxLength] are both set then [_checkLengthRange] will be applied.
/// All you need to do is setting these properties based on your needs.
///
/// [patterns] is for any no predefined validation rules.
/// The map key is regular expression pattern. The value is the error message for such validation.
/// If the pattern matches that means error.
class FormTextFieldValidator {
  FormTextFieldValidator(
    this.label,
    this.value, {
    this.label2,
    this.value2,
    this.isRequired = true,
    this.isPositiveNumber = false,
    this.isNumber = false,
    this.length,
    this.minLength,
    this.maxLength,
    this.minValue,
    this.maxValue,
    this.prefix,
    this.patterns,
  });

  final String nullOrEmptyValueErrorMessage = '%s不可为空';
  final String notPositiveNumberErrorMessage = '%s不是合法正整数';
  final String notNumberErrorMessage = '%s不是合法数字';
  final String sameValueErrorMessage = '%s和%s的值必须相同';
  final String lengthErrorMessage = '%s长度必须为%s';
  final String minValueErrorMessage = '%s的最小值为%s';
  final String maxValueErrorMessage = '%s的最大值为%s';
  final String valueRangeErrorMessage = '%s的值需在%s和%s之间';
  final String minLengthErrorMessage = '%s值的长度不能小于%s';
  final String maxLengthErrorMessage = '%s值的长度不能大于%s';
  final String lengthRangeErrorMessage = '%s值的长度需在%s和%s之间';
  final String startsWithErrorMessage = '%s的值必须以%s开头';

  final String? label;
  final dynamic value;

  // These for check same value.
  final String? label2;
  final dynamic value2;

  final bool? isRequired;
  final bool isPositiveNumber;
  final bool isNumber;
  final int? length;
  final int? minLength;
  final int? maxLength;
  final int? minValue;
  final int? maxValue;
  final String? prefix;
  final List<ValidatorPattern>? patterns;

  /// Starts to validate.
  String? validate() {
    String? result;

    if (isRequired!) {
      result = _checkNullOrEmpty(label, value);
    }

    // The input method might still can input emotion symbol even the keyboard type is number.
    // That's why we still need to do check.
    if (result.isNullOrEmpty && isPositiveNumber) {
      result = _checkPositiveNumber(label, value);
    }

    // The input method might still can input emotion symbol even the keyboard type is number.
    // That's why we still need to do check.
    if (result.isNullOrEmpty && isNumber) {
      result = _checkNumber(label, value);
    }

    if (result.isNullOrEmpty && value2 != null) {
      result = _checkSameValue(label, value, label2, value2);
    }

    if (result.isNullOrEmpty && length != null) {
      result = _checkLength(label, value, length);
    }

    if (result.isNullOrEmpty && minLength != null && maxLength == null) {
      result = _checkMinLength(label, value, minLength!);
    }

    if (result.isNullOrEmpty && minLength == null && maxLength != null) {
      result = _checkMaxLength(label, value, maxLength!);
    }

    if (result.isNullOrEmpty && minLength != null && maxLength != null) {
      result = _checkLengthRange(label, value, minLength!, maxLength!);
    }

    if (result.isNullOrEmpty && minValue != null && maxValue == null) {
      result = _checkMinValue(label, value, minValue!);
    }

    if (result.isNullOrEmpty && minValue == null && maxValue != null) {
      result = _checkMaxValue(label, value, maxValue!);
    }

    if (result.isNullOrEmpty && minValue != null && maxValue != null) {
      result = _checkValueRange(label, value, minValue!, maxValue!);
    }

    if (result.isNullOrEmpty && prefix.isNotNullOrEmpty) {
      result = _checkStartsWith(label, value, prefix!);
    }

    if (result.isNullOrEmpty && patterns != null) {
      result = _checkOnPatterns(label, value, patterns!);
    }

    return result;
  }

  /// Validates input value as mathematical expression.
  ExpressionValidationResult validateExpression() {
    String errorWrongNumberOrExpression = '$label不是合法的数字或公式';
    String? errorMessage;
    dynamic expressionResult;

    try {
      expressionResult = const ExpressionEvaluator()
          .eval(Expression.parse(value!), {})
          .toDouble();

      if (expressionResult == double.infinity ||
          expressionResult == double.negativeInfinity) {
        expressionResult = null;
        errorMessage = errorWrongNumberOrExpression;
      }
    } catch (e) {
      errorMessage = errorWrongNumberOrExpression;
    }

    return ExpressionValidationResult(
      expressionResult?.toDouble(),
      errorMessage,
    );
  }

  /// Validates if the input [value] is null or empty.
  String? _checkNullOrEmpty<T>(String? label, T value) {
    if (value == null || (value is String && value.trim().isEmpty)) {
      return sprintf(nullOrEmptyValueErrorMessage, [label]);
    }

    return null;
  }

  /// Validates if the input [value] is a valid positive number.
  String? _checkPositiveNumber(String? label, String value) {
    if (value.isNotValidPositiveNumber) {
      return sprintf(notPositiveNumberErrorMessage, [label]);
    }

    return null;
  }

  /// Validates if the input [value] is a valid number.
  String? _checkNumber(String? label, String value) {
    if (value.isNotValidNumber) {
      return sprintf(notNumberErrorMessage, [label]);
    }

    return null;
  }

  /// Validates if the input [value1] and [value2] have the same value.
  String? _checkSameValue<T>(
    String? label1,
    T value1,
    String? label2,
    T value2,
  ) {
    if (value1 != value2) {
      return sprintf(sameValueErrorMessage, [label1, label2]);
    }

    return null;
  }

  /// Validates if the input [value.length] equals [length].
  String? _checkLength<T>(String? label, T value, int? length) {
    if (value == null || (value is String && value.length != length)) {
      return sprintf(lengthErrorMessage, [label, length]);
    }

    return null;
  }

  /// Validates if the input [value.length] is less than [minLength].
  String? _checkMinLength(String? label, String value, int minLength) {
    if (value.length < minLength) {
      return sprintf(minLengthErrorMessage, [label, minLength]);
    }

    return null;
  }

  /// Validates if the input [value.length] is greater than [maxLength].
  String? _checkMaxLength(String? label, String value, int maxLength) {
    if (value.length > maxLength) {
      return sprintf(maxLengthErrorMessage, [label, maxLength]);
    }

    return null;
  }

  /// Validates if the input [value.length] is between [minLength] and [maxLength].
  String? _checkLengthRange(
    String? label,
    String value,
    int minLength,
    int maxLength,
  ) {
    assert(minLength < maxLength);

    if (value.length < minLength || value.length > maxLength) {
      return sprintf(lengthRangeErrorMessage, [label, minLength, maxLength]);
    }

    return null;
  }

  /// Validates if the input [value] is less than [minValue].
  String? _checkMinValue<T>(String? label, T value, int minValue) {
    if (int.parse(value.toString()) < minValue) {
      return sprintf(minValueErrorMessage, [label, minValue]);
    }

    return null;
  }

  /// Validates if the input [value] is greater than [maxValue].
  String? _checkMaxValue<T>(String? label, T value, int maxValue) {
    if (int.parse(value.toString()) > maxValue) {
      return sprintf(maxValueErrorMessage, [label, maxValue]);
    }

    return null;
  }

  /// Validates if the input [value] is between [minValue] and [maxValue].
  String? _checkValueRange<T>(
    String? label,
    T value,
    int minValue,
    int maxValue,
  ) {
    assert(minValue < maxValue);

    final intValue = int.parse(value.toString());

    if (intValue < minValue || intValue > maxValue) {
      return sprintf(valueRangeErrorMessage, [label, minValue, maxValue]);
    }

    return null;
  }

  /// Validates if the input [value] starts with [prefix].
  String? _checkStartsWith(String? label, String value, String prefix) {
    if (!value.startsWith(prefix)) {
      return sprintf(startsWithErrorMessage, [label, prefix]);
    }

    return null;
  }

  /// Iterates the [patterns] checks on each item's key which is regular expression.
  /// This is for any no predefined validation usage.
  /// If the pattern matches that means error.
  String? _checkOnPatterns(
    String? label,
    String? value,
    List<ValidatorPattern> patterns,
  ) {
    String? errorMessage;
    ValidatorPattern item;

    for (var i = 0; i < patterns.length; i++) {
      item = patterns[i];
      final regExp = RegExp(item.pattern);

      if ((item.matchCheck && regExp.hasMatch(value!)) ||
          (!item.matchCheck && !regExp.hasMatch(value!))) {
        errorMessage = item.errorMessage;
        break;
      }
    }

    return errorMessage;
  }
}
